package org.syncsoft.overalt.common.json;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.syncsoft.overalt.common.api.Parameter;

public class JSonTree {
	private String entityName;
	private Map<String, Parameter> parametersMap;
	private List<JSonTree> entities;
	private JSonTree currentEntity;
	
	private String key;
	
	{
		setEntities(new ArrayList<JSonTree>());
		setParametersMap(new HashMap<String, Parameter>());
	}
	
	public JSonTree() {
		setEntityName("main");
	}
	
	public JSonTree(String entityName) {
		setEntityName(entityName);
	}
	
	/**
	 * add key entity or parameter
	 * @param key
	 */
	public void addKey(String key) {
		if (getCurrentEntity() != null) {
			getCurrentEntity().addKey(key);
		} else {
			setKey(key);
		}
	}
	
	/**
	 * @precondition addKey was executed
	 * 
	 * @param value
	 */
	public void addParameter(String value) {
		if (getCurrentEntity() != null) {
			getCurrentEntity().addParameter(value);
		} else if (getKey() != null && value != null) {
			addParameter(getKey(), value);
		}
		
	}
	
	/**
	 * @precondition key != null && value != null
	 * 
	 * @param key
	 * @param value
	 */
	public void addParameter(String key, String value) {
		Parameter parameter = new Parameter(key, value);
		addParameter(parameter);
	}
	
	/**
	 * @precondition parameter != null
	 * @param parameter
	 */
	public void addParameter(Parameter parameter) {
		if (parameter != null) {
			if (getCurrentEntity() != null) {
				getCurrentEntity().addParameter(parameter);
			} else {
				getParametersMap().put(parameter.getKey(), parameter);
			}
		}
	}
	
	/**
	 * @precondition entityName != null
	 * @param entityName
	 */
	public void openEntity(String entityName) {
		if (entityName != null) {
			if (getCurrentEntity() == null) {
				setCurrentEntity(new JSonTree(entityName));
			} else {
				getCurrentEntity().openEntity(entityName);
			}
		}
	}
	
	/**
	 * @postcondition create a entity with the last entityName used
	 */
	public void openEntity() {
		if (getCurrentEntity() != null) {
			getCurrentEntity().openEntity();
		} else if (getKey() != null) {
			openEntity(getKey());
		}
	}
	
	/**
	 * @precondition entityName != null
	 * @postcondition a empty collection if don't found the jsonTree with this name
	 * 
	 * @param entityName
	 * @return
	 */
	public List<JSonTree> getJsonTreesFronEntityName(String entityName) {
		List<JSonTree> jsonTrees = new ArrayList<JSonTree>();
		
		if (entityName != null) {
			if (entityName.equals(this.getEntityName())) {
				jsonTrees.add(this);
			} else {
				getJsonTreeFromEntities(entityName, jsonTrees);
				
				if (jsonTrees.isEmpty()) {
					getJsonTreeInChildrenEntities(entityName, jsonTrees);
				}
			}
		} 
		
		return jsonTrees;
	}
	
	/**
	 * 
	 * @param entityName
	 * @return
	 */
	public JSonTree getJsonTreeFronEntityName(String entityName) {
		List<JSonTree> jsonTrees = getJsonTreesFronEntityName(entityName);
		JSonTree jsonTree = null;
		
		if (jsonTrees != null && !jsonTrees.isEmpty()) {
			jsonTree = jsonTrees.get(0);
		}
		
		
		return jsonTree;
	}
	
	/**
	 * @precondition parameterName != null
	 * @postcondition return null if not found the parameter with this name
	 * 
	 * @param parameterName
	 * @return the parameter with same name of parameterName
	 */
	public Parameter getParameter(String parameterName) {
		Parameter parameter = null;
		
		if (parameterName != null) {
			parameter = getParametersMap().get(parameterName);
		}
		
		return parameter;
	}
	
	/**
	 * @precondition parameterName != null
	 * @postcondition return null if not found the parameter with this name
	 * 
	 * @param parameterName
	 * @return the parameter value
	 */
	public String getParameterValue(String parameterName) {
		Parameter parameter = getParameter(parameterName);
		
		return parameter != null ? parameter.getValue() : null; 
	}

	/**
	 * @precondition entityName != null && jsonTree != null
	 * @postcondition jsonTrees will can be modified
	 * @param entityName
	 * @param jsonTrees
	 */
	private void getJsonTreeFromEntities(String entityName, Collection<JSonTree> jsonTrees) {
		for (JSonTree jsonTree : getEntities()) {
			if (entityName.equals(jsonTree.getEntityName())) {
				jsonTrees.add(jsonTree);
			}
		}
	}

	/**
	 * @precondition entityName != null && jsonTress != null
	 * @postcondition jsonTrees will can be modified
	 * 
	 * @param entityName
	 * @param jsonTrees
	 */
	private void getJsonTreeInChildrenEntities(String entityName, Collection<JSonTree> jsonTrees) {
		if (entityName != null && jsonTrees != null) {
			for (JSonTree jsonTree: getEntities()) {
				jsonTrees.addAll(jsonTree.getJsonTreesFronEntityName(entityName));
				
				if (!jsonTrees.isEmpty()) {
					break;
				}
			}
		}
	}
	
	/**
	 * @precondition getCurrentEntity != null
	 */
	public void closeEntity() {
		if (getCurrentEntity() != null) {
			if (getCurrentEntity().getCurrentEntity() == null) {
				getEntities().add(getCurrentEntity());
				getCurrentEntity().setKey(null);
				setCurrentEntity(null);
			} else {
				getCurrentEntity().closeEntity();
			}
		} else {
			setKey(null);
		}
	}
	
	@Override
	public String toString() {
		return getEntityName();
	}
	
	public String getEntityName() {
		return entityName;
	}
	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}
	public Collection<Parameter> getParameters() {
		return getParametersMap().values();
	}
	
	private Map<String, Parameter> getParametersMap() {
		return parametersMap;
	}
	private void setParametersMap(Map<String, Parameter> parametersMap) {
		this.parametersMap = parametersMap;
	}
	public List<JSonTree> getEntities() {
		return entities;
	}
	public void setEntities(List<JSonTree> entities) {
		this.entities = entities;
	}
	public JSonTree getCurrentEntity() {
		return currentEntity;
	}
	public void setCurrentEntity(JSonTree currentEntity) {
		this.currentEntity = currentEntity;
	}

	private String getKey() {
		return key;
	}

	private void setKey(String key) {
		this.key = key;
	}
}
